from typing import Callable, Dict, Any
from pathlib import Path
import sys
import re
import time
from vnsumscope.chart import widget

from PyQt5 import QtWidgets, Qsci, QtGui
from PyQt5.QtCore import QRegExp

from vnsumscope.event import EventEngine, Event
from ..utility import VNSUMSCOPE_CTA_STRATEGY_DIR, VNSUMSCOPE_PORTFOLIO_STRATEGY_DIR
from ..setting import SETTINGS


class CodeEditor():
    """"""
    _instance: "CodeEditor" = None

    def __new__(cls, *args, **kwargs):
        """"""
        if not cls._instance:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

    def open_editor(self, file_path: str = "") -> None:
        """"""
        # Show editor tab if file already opened
        if file_path:
            program = SETTINGS.get('editor', None)
            if program:
                if program == 'IDLE':
                    program = f'{str(Path(sys.prefix).joinpath("Lib", "idlelib", "idle.bat"))}'
                import subprocess
                subprocess.Popen(f'{program} {file_path}')
            else:
                import win32api
                win32api.ShellExecute(0, 'open', file_path, '', '', 1)


class AddStrategyDialog(QtWidgets.QDialog):
    """
    add strategy dialog, include two options for the dialog.
    """

    def __init__(self, event_engine: EventEngine):
        """
        Initialize the add strategy
        """
        super().__init__()

        self.projectpath = Path(__file__).parent.parent.parent

        self.cta_strategypath = Path(VNSUMSCOPE_CTA_STRATEGY_DIR)
        self.portfolio_strategypath = Path(VNSUMSCOPE_PORTFOLIO_STRATEGY_DIR)
        self.strategy_name_line = QtWidgets.QLineEdit()

        self.init_ui()

        self.widgets: Dict[str, Any] = {}

        self.strategy_name: QtWidgets.QLineEdit

        self.strategy_template: QtWidgets.QComboBox

        self.event_engine = event_engine

    def init_ui(self) -> None:
        """
        Initialize the new policy dialog box
        :return: None
        """
        self.setWindowTitle("新增策略")
        # 保证宽度
        min_width = max(QtWidgets.QApplication.desktop().width() / 8, 375)
        self.setMinimumWidth(min_width)

        # Initialize line and pull-down form layout.
        form = QtWidgets.QFormLayout()

        strategy_template_combo = QtWidgets.QComboBox()
        strategy_template_combo.addItems(["CTA策略", "组合策略"])

        self.strategy_name_line.setPlaceholderText("输入策略文件名，如:double_ma")

        form.addRow("策略模板", strategy_template_combo)
        form.addRow("文件名称", self.strategy_name_line)

        self.strategy_name = self.strategy_name_line
        self.strategy_template = strategy_template_combo
        button = QtWidgets.QPushButton("确定")
        button.clicked.connect(self.add_new_strategy)

        form.addRow(button)

        scroll_widget = QtWidgets.QWidget()
        scroll_widget.setLayout(form)

        scroll_area = QtWidgets.QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setWidget(scroll_widget)

        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(scroll_area)
        self.setLayout(vbox)

    def add_new_strategy(self) -> None:
        """
        add a specify strategy
        """
        # 校验策略文件输入框
        flag = self.message_error()
        if flag:
            # 根据下拉框的选项，到指定路径读取策略模板
            strategy_template = self.strategy_template.currentIndex()
            strategy_name = self.strategy_name.text()
            if not strategy_name.strip():
                QtWidgets.QMessageBox.information(self, '提示', '策略名称不能为空！', QtWidgets.QMessageBox.Yes)
                return None
            if strategy_template == 0:
                class_name = "CTAStrategy"
                strategy_path = self.cta_strategypath
                strategy_template_path = self.projectpath.joinpath("app", "cta_strategy", "cta_template.py")
            else:
                class_name = "PortFolioStrategy"
                strategy_path = self.portfolio_strategypath
                strategy_template_path = self.projectpath.joinpath("app", "portfolio_strategy", "portfolio_template.py")

            with open(strategy_template_path, "rb") as f:
                strategy_py = f.read()
            strategy_py = strategy_py.decode()
            strategy_py = strategy_py.replace(class_name, strategy_name.upper())
            strategy_py = strategy_py.encode()
            # 新建的策略名称格式为：策略名_模板名_时间戳（秒级别）
            new_strategy_name = strategy_name + ".py"
            new_strategy_filepath = strategy_path.joinpath(new_strategy_name)
            if new_strategy_filepath.exists():
                QtWidgets.QMessageBox.information(self, '提示', '策略文件名已经存在，请重新输入！', QtWidgets.QMessageBox.Yes)
                return None
            with open(new_strategy_filepath, "wb") as f:
                f.write(strategy_py)
            editor_class = CodeEditor()
            editor_class.open_editor(new_strategy_filepath)
            self.event_engine.put(Event('create_strategy', new_strategy_filepath))
            self.accept()

    def message_error(self) -> bool:
        strategy_name = self.strategy_name_line.text()
        if strategy_name.strip() == "":
            QtWidgets.QMessageBox.information(self, '提示', '策略名称不能为空', QtWidgets.QMessageBox.Yes)
        elif strategy_name.strip()[0].isdigit():
            QtWidgets.QMessageBox.information(self, '提示', '策略名称不能以数字开头', QtWidgets.QMessageBox.Yes)
        elif not re.match('^[0-9a-zA-Z_-]+$', strategy_name[1:]):
            QtWidgets.QMessageBox.information(self, '提示', '策略名称输入有误，必须为数字字母和下划线的组合，且不能以数字开头',
                                              QtWidgets.QMessageBox.Yes)
        elif len(strategy_name) > 32:
            QtWidgets.QMessageBox.information(self, '提示', '策略名称的长度不能超过32', QtWidgets.QMessageBox.Yes)
        else:
            return True
        return False


if __name__ == "__main__":
    from vnsumscope.trader.ui import create_qapp

    app = create_qapp()
    editor = CodeEditor()
    editor.show()
    app.exec_()
